package com.xh.hongaicodegenerator.langgraph4j.tools;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.util.Arrays;
import java.util.List;

/**
 * 代码文件读取工具类（同时对 {{}} 转义）
 */
@Slf4j
public class CodeFileReaderTool {

    private static final List<String> CODE_EXTENSIONS = Arrays.asList(
            ".html", ".css", ".js", ".json", ".vue", ".ts", ".jsx", ".tsx", ".md", ".svg", ".ico"
    );

    private static final List<String> EXCLUDED_FILES = Arrays.asList(
            "package-lock.json",
            "yarn.lock",
            "pnpm-lock.yaml"
    );

    private static final List<String> EXCLUDED_DIRS = Arrays.asList(
            "node_modules",
            "dist",
            "target",
            ".git"
    );

    /**
     * 读取并拼接代码目录下的所有代码文件，同时对 {{}} 进行转义
     *
     * @param codeDir 代码目录路径
     * @return 拼接后的代码内容
     */
    public static String readAndConcatenateCodeFiles(String codeDir) {
        if (StrUtil.isBlank(codeDir)) {
            return "";
        }

        File directory = new File(codeDir);
        if (!directory.exists() || !directory.isDirectory()) {
            log.error("代码目录不存在或不是目录: {}", codeDir);
            return "";
        }

        StringBuilder codeContent = new StringBuilder();
        codeContent.append("# 项目文件结构和代码内容\n\n");

        FileUtil.walkFiles(directory, file -> {
            if (shouldSkipFile(file, directory)) {
                return;
            }
            if (isCodeFile(file)) {
                String relativePath = FileUtil.subPath(directory.getAbsolutePath(), file.getAbsolutePath());
                codeContent.append("## 文件: ").append(relativePath).append("\n\n");

                // 读取文件内容并同时转义 {{ }}
                String fileContent = FileUtil.readUtf8String(file);
                fileContent = escapeCurlyBraces(fileContent);

                codeContent.append(fileContent).append("\n\n");
            }
        });

        return codeContent.toString();
    }

    /**
     * 将 {{ 和 }} 转义为 [[OPEN]] 和 [[CLOSE]]
     * 时间复杂度 O(n)
     *
     * @param input 输入字符串
     * @return 转义后的字符串
     */
    private static String escapeCurlyBraces(String input) {
        if (input == null || input.isEmpty()) {
            return input;
        }

        // 使用 StringBuilder 一次遍历替换
        StringBuilder sb = new StringBuilder(input.length());
        int length = input.length();
        for (int i = 0; i < length; i++) {
            char c = input.charAt(i);
            if (c == '{' && i + 1 < length && input.charAt(i + 1) == '{') {
                sb.append("[[OPEN]]");
                i++; // 跳过下一个 '{'
            } else if (c == '}' && i + 1 < length && input.charAt(i + 1) == '}') {
                sb.append("[[CLOSE]]");
                i++; // 跳过下一个 '}'
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    private static boolean shouldSkipFile(File file, File rootDir) {
        String relativePath = FileUtil.subPath(rootDir.getAbsolutePath(), file.getAbsolutePath());
        relativePath = relativePath.replace("\\", "/");

        if (file.getName().startsWith(".")) return true;
        if (EXCLUDED_FILES.contains(file.getName())) return true;

        for (String dir : EXCLUDED_DIRS) {
            if (relativePath.contains("/" + dir + "/") ||
                    relativePath.startsWith(dir + "/") ||
                    relativePath.equals(dir)) {
                return true;
            }
        }
        return false;
    }

    private static boolean isCodeFile(File file) {
        String fileName = file.getName().toLowerCase();
        return CODE_EXTENSIONS.stream().anyMatch(fileName::endsWith);
    }
}
